package com.example.cycledependency;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CycleDependency1 {
    private static final Map<String, RootBeanDefinition> beanDefinitionMap = new HashMap<>();
    // 一级缓存，即单例池
    private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();


    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        newApplicationContext();
        getBean("instanceA");
    }

    public static void newApplicationContext() throws InstantiationException, IllegalAccessException {
        // 加载所有的BeanDefinition到beanDefinitionMap中
        loadBeanDefinitions();
        // 遍历definitionMap去创建Bean
        for (String beanName : beanDefinitionMap.keySet()) {
            getBean(beanName);
        }
    }

    public static void loadBeanDefinitions() {
        RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);
        RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);
        beanDefinitionMap.put("instanceA", aBeanDefinition);
        beanDefinitionMap.put("instanceB", bBeanDefinition);
    }

    private static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {
        // 若一级缓存中有，说明已创建，直接返回
        if (singletonObjects.containsKey(beanName)) {
            return singletonObjects.get(beanName);
        }
        // 获取当前BeanDefinition
        RootBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 实例化Bean
        Class<?> beanClass = beanDefinition.getClass();
        Object beanInstance = beanClass.newInstance();
        // 放到一级缓存
        singletonObjects.put(beanName, beanInstance);
        // 属性注入(循环依赖)
        // 拿到Class中的所有属性
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            // 判断当前字段是否有@Autowired注解
            Autowired annotation = declaredField.getAnnotation(Autowired.class);
            // 说明属性有Autowired
            if (annotation != null) {
                String name = declaredField.getName();
                Object bean = getBean(name);
                declaredField.setAccessible(true);
                declaredField.set(beanInstance, bean);
            }
        }
        return beanInstance;
    }
}
// 该种方式存在的问题：
// 线程1调用getBean("instanceA");已经将instanceA放到单例池中了，但是其中的instanceB属性还没有注入
// 此时线程2调用getBean("instanceA");就可以获取单例池中的instanceA了，但是该Bean是不完整的